package com.support.mvc.entity.base;

import com.querydsl.core.QueryResults;
import com.utils.enums.Code;
import lombok.*;
import lombok.experimental.Accessors;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;

import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import java.util.Collections;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 构建分页对象
 *
 * @author 谢长春 on 2017/10/31.
 */
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class Page {

    public static Page defaultPage() {
        return Page.builder().build().init();
    }

    public static Page of(final int number, final int limit) {
        return new Page()
                .setNumber(number)
                .setLimit(limit);
    }

    public static PageRequest maxPageable(final Sort sort) {
        return Objects.isNull(sort)
                ? PageRequest.of(0, 1000)
                : PageRequest.of(0, 1000, sort);
    }

    /**
     * 上一页[最大|最小]的 ID 用于优化排序；谨慎使用：只能在使用 ID 排序时使用
     */
    private Long id;

    /**
     * 当前页码
     */
    @Min(1)
    private int number;
    /**
     * 每页大小
     */
    @Min(1)
    @Max(1000)
    private int limit;

    public static Page.PageBuilder builder() {
        return new Page.PageBuilder();
    }

    public static class PageBuilder {
        private Long id;
        private int number;
        private int limit;

        PageBuilder() {
        }

        public Page.PageBuilder id(Long id) {
            this.id = id;
            return this;
        }

        public Page.PageBuilder number(int number) {
            this.number = number;
            return this;
        }

        public Page.PageBuilder limit(int limit) {
            this.limit = limit;
            return this;
        }

        public Page build() {
            final Page page = new Page();
            page.setId(this.id);
            page.setNumber(this.number);
            page.setLimit(this.limit);
            return page;
        }
    }

    /**
     * 校验并初始化分页对象
     *
     * @return {@link Page}
     */
    public Page init() {
        number = number <= 0 ? 1 : number;
        limit = limit <= 0 ? 20 : limit;
        return this;
    }

    public Page assertSize() {
        Code.A00008.assertHasTrue(limit > 1000, "每页最多只能查询 1000 条数据");
        return this;
    }

    /**
     * 构建 JPA 查询分页对象
     *
     * @return {@link Pageable}
     */
    public Pageable pageable() {
        return PageRequest.of(number - 1, limit);
    }

    /**
     * 构建 JPA 查询分页对象
     *
     * @param sort {@link Sort}
     * @return {@link Pageable}
     */
    public Pageable pageable(final Sort sort) {
        return Objects.isNull(sort)
                ? PageRequest.of(number - 1, limit)
                : PageRequest.of(number - 1, limit, sort);
    }

    /**
     * QDSL 查询缩进量
     *
     * @return int
     */
    public int offset() {
        return (number - 1) * limit;
    }

    /**
     * QDSL 查询大小
     *
     * @return int
     */
    public int limit() {
        return limit;
    }

    /**
     * 切换到下一页并返回页码
     *
     * @return int 页码
     */
    public int next() {
        return ++number;
    }

    /**
     * {@link org.springframework.data.domain.Page} 转换为 {@link QueryResults}
     *
     * @return {@link QueryResults}
     */
    public <T> QueryResults<T> emptyQueryResults() {
        return new QueryResults<>(
                Collections.emptyList(),
                (long) limit(),
                (long) offset(),
                0
        );
    }

    /**
     * {@link org.springframework.data.domain.Page} 转换为 {@link QueryResults}
     *
     * @param page {@link org.springframework.data.domain.Page}
     * @return {@link QueryResults}
     */
    public <T> QueryResults<T> toQueryResults(final org.springframework.data.domain.Page<T> page) {
        return new QueryResults<>(
                page.getContent(),
                (long) limit(),
                (long) offset(),
                page.getTotalElements()
        );
    }

    /**
     * 将分页查询结果中的投影对象转换为新的分页对象
     *
     * @param results  {@link QueryResults}
     * @param function {@link Function<T, R>} 将投影对象转换为 R
     * @return {@link QueryResults}
     */
    public static <T, R> QueryResults<R> toQueryResults(final QueryResults<T> results, final Function<T, R> function) {
        return new QueryResults<>(
                results.getResults().stream().map(function).collect(Collectors.toList()),
                results.getLimit(),
                results.getOffset(),
                results.getTotal()
        );
    }

}
